/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * language-support-plugin
 * 
 * language-support-plugin is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * language-support-plugin is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <gio/gio.h>
#include <string.h>

#include "local-symbol.h"
#include "dir-symbol.h"
#include "util.h"

typedef struct _DirSymbolPrivate DirSymbolPrivate;
struct _DirSymbolPrivate
{
	GList *member;
	gchar *name;
	gchar *self_name;
};

#define DIR_SYMBOL_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), DIR_TYPE_SYMBOL, DirSymbolPrivate))

static void dir_symbol_interface_init (IJsSymbolIface *iface);
static GList* dir_symbol_get_arg_list (IJsSymbol *obj);
static gint dir_symbol_get_base_type (IJsSymbol *obj);
static GList* dir_symbol_get_func_ret_type (IJsSymbol *obj);
static IJsSymbol* dir_symbol_get_member (IJsSymbol *obj, const gchar * name);
static const gchar * dir_symbol_get_name (IJsSymbol *obj);
static gboolean dir_symbol_is_deleted (IJsSymbol *obj);
static GList* dir_symbol_list_member (IJsSymbol *obj);

G_DEFINE_TYPE_WITH_CODE (DirSymbol, dir_symbol, G_TYPE_OBJECT,
			G_IMPLEMENT_INTERFACE (IJS_TYPE_SYMBOL,
						dir_symbol_interface_init));

static void
dir_symbol_init (DirSymbol *object)
{
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (object);
	priv->member = NULL;
	priv->name = NULL;
	priv->self_name = NULL;
}

static void
dir_symbol_finalize (GObject *object)
{
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (object);

	g_free (priv->name);
	g_free (priv->self_name);
	g_list_foreach (priv->member, g_object_unref, NULL);

	G_OBJECT_CLASS (dir_symbol_parent_class)->finalize (object);
}

static void
dir_symbol_class_init (DirSymbolClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	GObjectClass* parent_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (DirSymbolPrivate));

	object_class->finalize = dir_symbol_finalize;
}


DirSymbol*
dir_symbol_new (const gchar* dirname)
{
	DirSymbol* self = DIR_SYMBOL (g_object_new (DIR_TYPE_SYMBOL, NULL));
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (self);

	GFileInfo *info;
	GFile *dir = g_file_new_for_path (dirname);
	priv->self_name = g_file_get_basename (dir);

	if (!priv->self_name || priv->self_name[0] == '.')
		return NULL;

	GFileEnumerator *enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, NULL, NULL);

	priv->member = NULL;

	for (info = g_file_enumerator_next_file (enumerator, NULL, NULL); info; info = g_file_enumerator_next_file (enumerator, NULL, NULL))
	{
		const gchar *name = g_file_info_get_name (info);
		if (!name)
			continue;
		GFile *file = g_file_get_child (dir, name);
		if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY)
		{
			DirSymbol *t = dir_symbol_new (g_file_get_path (file));
			if (t)
				priv->member = g_list_append (priv->member, t);
			continue;
		}
		int len = strlen (name);
		if (len <= 3 || strcmp (name + len - 3, ".js") != 0)
			continue;
		IJsSymbol* t = NULL;
		t = db_anjuta_symbol_new (g_file_get_path (file));
		if (!t)
				t = IJS_SYMBOL (local_symbol_new (g_file_get_path (file)));
		if (!t)
			continue;
		priv->member = g_list_append (priv->member, t);
	}
	if (!priv->member)
		return NULL;
	priv->name = g_strdup (dirname);

	return self;
}

static void
dir_symbol_interface_init (IJsSymbolIface *iface)
{
	iface->get_arg_list = dir_symbol_get_arg_list;
	iface->get_base_type = dir_symbol_get_base_type;
	iface->get_func_ret_type = dir_symbol_get_func_ret_type;

	iface->get_member = dir_symbol_get_member;
	iface->get_name = dir_symbol_get_name;
	iface->is_deleted = dir_symbol_is_deleted;
	iface->list_member = dir_symbol_list_member;
}

static GList*
dir_symbol_get_arg_list (IJsSymbol *obj)
{
	g_assert_not_reached ();
}

static gint
dir_symbol_get_base_type (IJsSymbol *obj)
{
	return BASE_CLASS;
}

static GList*
dir_symbol_get_func_ret_type (IJsSymbol *obj)
{
	g_assert_not_reached ();
}

static IJsSymbol*
dir_symbol_get_member (IJsSymbol *obj, const gchar * name)
{
	DirSymbol* self = DIR_SYMBOL (obj);
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (self);

	GList *i;
	for (i = priv->member; i; i = g_list_next (i))
	{
		IJsSymbol *t = IJS_SYMBOL (i->data);
		if (strcmp (name, ijs_symbol_get_name (t)) == 0 )
			return t;
	}
	return NULL;
}

static const gchar *
dir_symbol_get_name (IJsSymbol *obj)
{
	DirSymbol* self = DIR_SYMBOL (obj);
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (self);

	return g_strdup (priv->self_name);
}

static gboolean
dir_symbol_is_deleted (IJsSymbol *obj)
{
	return TRUE;
}

static GList*
dir_symbol_list_member (IJsSymbol *obj)
{
	DirSymbol* self = DIR_SYMBOL (obj);
	DirSymbolPrivate* priv = DIR_SYMBOL_PRIVATE (self);

	GList *i;
	GList *ret = NULL;
	for (i = priv->member; i; i = g_list_next (i))
	{
		IJsSymbol *t = IJS_SYMBOL (i->data);
		ret = g_list_append (ret, g_strdup (ijs_symbol_get_name (t)));
	}
	return ret;
}
